1. Field of the Invention
This invention relates to the transfer of file descriptors, in particular socket descriptors, from a first server process to a second server process, in particular, to a thread running in one of multiple server processes in a multiprocess, multithreaded client/server system.
2. Description of the Related Art
Client/server systems are well known in the field of data processing. In a client/server system, a client process issues a request to a server process to perform a service for it. In response, the server transmits a reply to the client, notifying it of the results of the service.
There are many existing server applications which developers want to be able to execute on server systems of different capacities. These server applications need to scale with the size of the computer they are running on. The present invention addresses the problem of how to support thousands of concurrent requests to a server.
There are two existing models for supporting many clients making requests to servers. The first model has the server create a separate process or address space for each client (typically with a fork( ) or spawn( ) system call). The second model has the server create a separate thread or task for each client (as with a pthread_create( ) system call). Each of these models has its problems.
The process model suffers from several disadvantages. Creating each process tends to result in a lot of system overhead. Once created, each process also consumes significant system resources such as storage. An operating system generally has limits on how many processes it can support. For many client/server relationships, a client is idle for long periods. In this situation it is an unacceptable waste of resources to tie up an address space for the client. In the process model, the server typically establishes a connection to the client by establishing a socket connection. The socket is represented by a socket descriptor which is inherited by the child process on a fork( ) or spawn( ) system call.
For the threading model, there are different problems. The number of threads that can be supported in a single process can be limited by several factors. The storage consumed by a thread and serialization between threads are the major limitations. In the threading model, the main thread in the server typically establishes a connection to the client by establishing a socket connection. The socket is represented by a socket descriptor which is shared by all the threads in the server process.
As a specific example of the general problem, consider a system that can support 400 processes, each of which can support a maximum of 200 threads. If the server needs to support 4000 clients, then neither the process model nor the thread model are sufficient.
In order to solve this problem, it is necessary to distribute the workload across multiple processes. These processes act as an extension to the initial server. In order to distinguish which process is being discussed, the initial server is called the "listening daemon", since its job is to listen for client connections. The processes which perform the client work are simply called "server processes" or just "servers".
In the above example, one would end up with a listening daemon and 20 servers each running 200 threads. In this arrangement, one can vary the number of servers and the number of threads in each server to achieve optimum performance.
The fork( ) and spawn( ) services for creating a new process and the pthread_create( ) service for creating a new thread provide for the mechanism to either pass or share a file descriptor, including those representing a socket. However, these services are unavailable if a server space has been created before the connection to the client is established, as in the environment of the present invention. A new mechanism is therefore needed for a listening daemon to pass the connection to the thread where the client request will be run.
This problem has been previously solved, but in a manner that provides unacceptable performance in certain environments. Thus, in one method the listening daemon accepts a conversation. It then uses some workload balancing algorithm to determine where the client should really be connected. In this case, each server may own a separate port. The listening daemon then sends a response back to the client telling it to reconnect to a particular port which gets the client to an available server.
Another method requires that the listening daemon maintain a separate communication path (pipe) to each server. When the listening daemon receives a connection request, it needs to pass information to the selected server. The listening daemon then breaks the connection to the client, while the server reestablishes the connection.
In both of the methods described, there is double the overhead in establishing a connection between the client and the server that actually performs the work. In some servers, this is acceptable, since the servers are connection oriented. That means that the server maintains the connection to the client for a long period of time in comparison to the overhead necessary to establish the connection. Other client/server systems, however, use a transactional model. In this model, the client makes a request which establishes a connection only for the duration of the single request. The World Wide Web is an example of a transactional model. In the transactional model, the overhead of the double connection can easily be more that the overhead of running the transaction.
A better method is needed to handle the transfer of a socket connection from a listening daemon to a server that is running a transaction model.